package server;

import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.*;

public class Digester {

    public Object parse(InputStream input) throws Exception {
        SAXReader saxReader = new SAXReader();

        Document document = saxReader.read(input);
        Element rootElement = document.getRootElement();

        Node serverNode = rootElement.selectSingleNode("//Server");

        Server server = new Server();
        List<Element> serviceNodes = serverNode.selectNodes("//Service");

        Service[] services = new Service[serviceNodes.size()];

        for (int i = 0; i < serviceNodes.size(); i++) {
            Element serviceNode = serviceNodes.get(i);
            Service service = new Service();
            List<Element> connectorNodes = serviceNode.selectNodes("//Connector");
            Object[] connectors = parseConnector(connectorNodes);

            service.setConnectors((Connector[]) connectors);

            Node engineNode = serviceNode.selectSingleNode("//Engine");
            Engine engine = new Engine();

            List<Element> hostNodes = engineNode.selectNodes("//Host");
            Host[] hosts = (Host[]) parseHost(hostNodes);
            Arrays.stream(hosts).forEach(host -> {
                loadServlet(host);
                engine.addHost(host.getName(), host);
            });

            service.setEngine(engine);

            services[i] = service;
        }
        server.setServices(services);

        return server;
    }

    private void loadServlet(Host host) {

        String appBase = host.getAppBase();
        File f = new File(appBase);
        if (!f.isAbsolute()) {
            String path = Bootstrap.getMincatHomeFile().getAbsolutePath() + "/" + appBase;
            f = new File(path);
        }
        if (f.exists()) {
            if (f.isDirectory()) {
                File[] webapps = f.listFiles();
                if (webapps != null && webapps.length > 0) {
                    Arrays.stream(webapps).forEach(webapp -> {
                        if (webapp.isDirectory()) {
                            String webappName = webapp.getName();
                            String docBase = webapp.getAbsolutePath();
                            Context context = new Context(webappName, docBase);
                            host.addContext(webappName, context);

                            // 查找底下的web.xml
                            String webConfPath = docBase + File.separatorChar + "WEB-INF";

                            Map<String, Wrapper> wrappers = loadServlet(webConfPath);
                            context.setWrappers(wrappers);
                        }
                    });
                }
            }
        }
    }


    private Map<String, Wrapper> loadServlet(String path) {
        InputStream resourceAsStream = null;
        try {
            resourceAsStream = new FileInputStream(path + File.separatorChar + "web.xml");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        SAXReader saxReader = new SAXReader();

        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            Map<String, Wrapper> wrapperMap = new HashMap<>();
            for (int i = 0; i < selectNodes.size(); i++) {
                Element element = selectNodes.get(i);
                // <servlet-name>lagou</servlet-name>
                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletnameElement.getStringValue();
                // <servlet-class>server.LagouServlet</servlet-class>
                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletclassElement.getStringValue();


                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                // /lagou
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

                File f = new File(path +  File.separatorChar + "classes");
                Object instance = new MinicatClassLoader(f.getAbsolutePath()).findClass(servletClass).getConstructor().newInstance();


                wrapperMap.putIfAbsent(urlPattern, new Wrapper(urlPattern, (HttpServlet) instance));
            }
            return wrapperMap;
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    private Object[] parseHost(List<Element> hostNodes) {
        Host[] hosts = new Host[hostNodes.size()];
        for (int i = 0; i < hostNodes.size(); i++) {
            Element hostNode = hostNodes.get(i);
            Attribute name = hostNode.attribute("name");
            Attribute appBase = hostNode.attribute("appBase");
            hosts[i] = new Host(name.getStringValue(), appBase.getStringValue());
        }
        return hosts;
    }

    private Object[] parseConnector(List<Element> connectorNodes) {
        Connector[] connectors = new Connector[connectorNodes.size()];
        for (int i = 0; i < connectorNodes.size(); i++) {
            Element connectorNode = connectorNodes.get(i);
            Attribute port = connectorNode.attribute("port");
            String portVal = port.getValue();
            connectors[i] = new Connector(Integer.parseInt(portVal));
        }
        return connectors;
    }
}
